33. Spring5 - 注解配置 bean

注解配置 bean

普通组件:@Component,标识一个受Spring IOC容器管理的组件

持久化层组件:@Repository,标识一个受Spring IOC容器管理的持久化层组件

业务逻辑层组件:@Service,标识一个受Spring IOC容器管理的业务逻辑层组件

表述层控制器组件:@Controller,标识一个受Spring IOC容器管理的表述层控制器组件

@Repository,@Service,@Controller 都是由@Component 演化而来,他们之间的功能都是一样的,也就说可以用 @Component 的地方,其他三个也可以,区别就是名称不一样。

创建 Service

1
2
3
package com.itguigu.ioc.userMod.service;

public interface UserService {}
1
2
3
4
5
6
7
8
9
10
11
package com.itguigu.ioc.userMod.service;

import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService{

public UserServiceImpl() {
System.out.println("UserServiceImpl");
}
}

创建 Controller

1
2
3
4
5
6
7
8
9
10
11
package com.itguigu.ioc.userMod.controller;

import org.springframework.stereotype.Controller;

@Controller
public class UserController {

public UserController() {
System.out.println("UserController");
}
}

创建 Dao

1
2
3
package com.itguigu.ioc.userMod.dao;

public interface UserDao {}
1
2
3
4
5
6
7
8
9
10
11
package com.itguigu.ioc.userMod.dao;

import org.springframework.stereotype.Repository;

@Repository
public class UserDaoImpl implements UserDao{

public UserDaoImpl() {
System.out.println("UserDaoImpl");
}
}

创建 xml

需要在 xml 中扫描组件,这样 Spring 才能管理有 @Controller,@Service,@Respository 的类,让其成为 spring 能管理的组件(bean)。也就是说加上注解后,Spring 会自动生成相应的 bean。且生成的 bean 的 id 是类名首字母的小写,即 userController, userServiceImpl, userDaoImpl。

base-package 属性指定一个需要扫描的基类包,Spring容器将会扫描这个基类包及其子包中的所有类。

当需要扫描多个包时可以使用逗号分隔。

如果仅希望扫描特定的类而非基包下的所有类,可使用resource-pattern属性过滤特定的类

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">

<!-- 扫描组件,这样 Spring 才能管理有 @Controller,@Service,@Respository 的类,让其成为 spring 能管理的组件(bean) -->
<!-- 也就是说加上注解后,Spring 会自动生成相应的 bean。且生成的 bean 的 id 是类名首字母的小写,即 userController, userServiceImpl, userDaoImpl -->
<!-- base-package 是包路径 -->
<context:component-scan base-package="com.itguigu.ioc.userMod"></context:component-scan>

</beans>

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.itguigu.ioc.userMod;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.itguigu.ioc.userMod.controller.UserController;
import com.itguigu.ioc.userMod.dao.UserDao;
import com.itguigu.ioc.userMod.dao.UserDaoImpl;
import com.itguigu.ioc.userMod.service.UserService;
import com.itguigu.ioc.userMod.service.UserServiceImpl;


public class Test {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("user.xml");
// 因为默认 spring 管理的 bean 都是单例的,即加载的时候就会创建其对象,不是 getBean 的时候才创建对象
// 所以运行后就能看到在构造器中的输出 UserDaoImpl UserController UserServiceImpl

// Spring 自动生成的 bean 的 id 是类名首字母的小写,即 userController, userServiceImpl, userDaoImpl
// 这里可以进行 bean 的获取,看是否能获取到
UserController userController = applicationContext.getBean("userController", UserController.class);
System.out.println(userController);

UserService userService = applicationContext.getBean("userServiceImpl", UserServiceImpl.class);
System.out.println(userService);

UserDao userDao = applicationContext.getBean("userDaoImpl", UserDaoImpl.class);
System.out.println(userDao);
}
}

组件扫描范围

context:include-filter 的作用是,在指定的包结构下,再次通过注解或者类型的方式来指定需要包含的类。需要注意的是,需要将 use-default-filters 的值设置为 false,即关闭默认的过滤(默认是扫描包下所有的类)。

1
2
3
4
5
6
7
8
<context:component-scan base-package="com.itguigu.ioc.userMod" use-default-filters="false">
<!-- 使用 include-filter 的时候,需要将 use-default-filters 设置为 false, 默认为 true 是全部扫描 -->
<!-- annotation: 是根据注解类型进行设置。这里允许扫描 com.itguigu.ioc.userMod 包下,有 Controller 类型的注解类 -->
<!-- <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> -->

<!-- assignable: 是根据类名称进行设置。这里允许扫描 com.itguigu.ioc.userMod.service.UserServiceImpl -->
<context:include-filter type="assignable" expression="com.itguigu.ioc.userMod.service.UserServiceImpl"/>
</context:component-scan>

context:include-filter 的作用是,在指定的包结构下,再次通过注解或者类型的方式来排除不需要包含的类。需要注意的是,需要将 use-default-filters 的值设置为 true,即允许默认的过滤(扫描包下所有的类)

1
2
3
4
5
6
7
<context:component-scan base-package="com.itguigu.ioc.userMod" use-default-filters="true">
<!-- 使用 exclude-filter 的时候,需要将 use-default-filters 设置为 false,或者直接去掉 -->
<!-- annotation: 是根据注解类型进行设置。这里不允许扫描 com.itguigu.ioc.userMod 包下,有 Repository 类型的注解的类 -->
<!-- <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/> -->

<context:exclude-filter type="assignable" expression="com.itguigu.ioc.userMod.dao.UserDaoImpl"/>
</context:component-scan>

注意⚠️:context:include-filter 中可以有多个包含,context:include-filter 下也可以有多个排除,但是两者不能同时使用。因为使用包含,那么剩下的就是不包含的,不包含的就没有必要在写出来了,反之同理。使用不包含,那么剩下的就是需要包含的。且 use-default-filts 在包含和不包含的情况下值不一样。

注解自动装配

在以前 controller 调用 service,service 调用 dao 的时候,都需要先实例话对象,然后在 对象.方法名 进行就行具体的调用,但是现在使用注解后我们可以直接使用 *@Autowired* 来代替以前的实例话对象。

controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.itguigu.ioc.userMod.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

import com.itguigu.ioc.userMod.service.UserService;

@Controller
public class UserController {

@Autowired // 基于注解的自动注入
private UserService userService;

public void addUser() {
userService.addUser();
}

public UserController() {
System.out.println("UserController");
}
}

service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.itguigu.ioc.userMod.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.itguigu.ioc.userMod.dao.UserDao;


@Service
public class UserServiceImpl implements UserService{

@Autowired // 基于注解的自动注入
private UserDao userDao;

public UserServiceImpl() {
System.out.println("UserServiceImpl");
}

@Override
public void addUser() {
userDao.adduser();
}
}

注解装配总结

组件:指 Spring 管理的 bean

自动装配:在需要赋值的非字面量属性上,加上 @Autowired,就可以在 Spring 容器中,通过不同的方式 (默认 byType,还可以 byName,或者多个时使用 Qualifier 指定) 匹配到相对应的 bean。

@Autowired 装配

  1. 会默认使用 byType 的方式,此时要求 Spring 容器中有且只有一个能够为其赋值,否则会报错(NoUniqueBeanDefinitionException)。

  2. 当 byType 实现不了装配时,会自动切换到 byName,此时要求 Spring 容器中有一个 bean 的 id 需要和属性名称一致。

  3. 若自动装配时匹配到多个可以赋值的 bean,我们可以使用 @Qualifier(value="bean id") 来指定使用哪一个 bean。

  4. @Autowired 和 @Qualifier 可以一起做用于一个带形参的方法上,此时,@Qualifier(value=”bean id”) 所指定的 bean 作用于形参。

  5. 可以在四个注解后面直接设置该注解的 id。即 @Repository(“bean id”) …
  6. @Autowired 上可以设置 reqired 属性,当 @Autowired(required = false) 时,如果装配不成功则不会报错,但是需要注意调用方法出现空指针异常。

基于注解的组件化管理

  1. @Repository,@Service,@Controller,@Component 四个注解功能完全相同,不过实际开发中,要在实现不同功能的类上加上相应的注解。

  2. 完成组件化管理的过程:

    1. 在需要被 Spring 管理的类上加上相应的注解
    2. 在配置文件中通过 <context:component-scan> 对所设置的包结构进行扫描,就会将加上注解的类,作为 Spring 的组件进行加载(会自动在 Spring 的配置文件中生成相对应的 bean,这些 bean 的 id 就是类名称的首字母小写。)

@Resource 和 @Autowired 功能一样,只是默认加载顺序不一样,@Autowired 是先 byType 然后 byName。而 @Resource 则相反。

@Inject 和 @Autowired 注解一样也是按类型注入匹配的 bean,但没有 reqired 属性。

代码地址